/* SSP Plugin (Stevens Semantic Signal Processing Project)

   Copyright (C) 2011 xxu7@stevens.edu

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */


#include <config.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>



#include "gcc-plugin.h"
#include "plugin.h"
#include "plugin-version.h"
#include "double-int.h"


#include "ssp-plugin.h"


static void print_tree_code_name(tree x)
{
    if(x != NULL_TREE){
	unsigned int tc = TREE_CODE(x);
	printf("Debug: Tree_code is %s @ PHY_ADDR %u in %d Bytes\n",
		   tree_code_name[tc], (unsigned int)x, (int)tree_size(x));
    }
    else
        printf("\n");
}

char *SSPC_fun_call_name(tree t)
{
	if (TREE_CODE(t) != ADDR_EXPR)
	{
		printf("ERROR: not ADDR_EXPR in fun_call_name \n");
		return NULL;
	}
	else
	{
		return IDENTIFIER_POINTER(DECL_NAME(TREE_OPERAND(t, 0)));
	}
}

static tree 
ssp_func_walk(tree * tp, int *ws, void *data)
{
	tree t = *tp;
	sspc_data *sdata = (sspc_data *) data;
        //print_tree_code_name(t); 
	if (TREE_CODE(t) == STATEMENT_LIST)
	{
		SSPC_hook_stmt_fun(&t, sdata, ws);
	}
	else if (TREE_CODE(t) == BIND_EXPR)
	{
		SSPC_hook_expr_fun(&t, sdata, ws);
	}
	else if ( TREE_CODE(t) == CALL_EXPR )	/* meet call expr node */
	{
		SSPC_hook_call_fun(&t, sdata, ws);
	}
	else if (EXPR_P(t))			/* meet expression node */
	{
		SSPC_hook_expr_fun(&t, sdata, ws);
	}
	else if (DECL_P(t))			/* meet declaration node */
	{
		SSPC_hook_decl_fun(&t, sdata, ws);
	}
	else if (CONSTANT_CLASS_P(t))	/* meet constant */
	{
		SSPC_hook_const_fun(&t, sdata, ws);
	}
	else
	{
		printf("//error, not in my consideration\n//");
	}
	return NULL;
}

static tree
ssp_func_trace(tree *tp, int *ws, void *data){

    tree t = *tp;
    sspc_data *sdata = (sspc_data *) data;
    print_tree_code_name(t);
    if( TREE_CODE(t) == VAR_DECL )
    {
        //print_tree_code_name(t);
        if (DECL_NAME(t))
            printf("Name = %s\n", IDENTIFIER_POINTER(DECL_NAME(t)) );
        else
            printf("NAME = TEMP-%d\n", DECL_UID(t));
    }
    else if ( TREE_CODE(t) == PARM_DECL)
    { 
        //print_tree_code_name(t);
        if (DECL_NAME(t))
            printf("Name = %s\n", IDENTIFIER_POINTER(DECL_NAME(t)) );
        else
            printf("NAME = TEMP-%d\n", DECL_UID(t));
    }
    else if ( TREE_CODE(t) == FIELD_DECL)
    { 
        //print_tree_code_name(t);
        if (DECL_NAME(t))
            printf("Name = %s\n", IDENTIFIER_POINTER(DECL_NAME(t)) );
        else
            printf("NAME = TEMP-%d\n", DECL_UID(t));
    } 
    else if ( TREE_CODE(t) == RESULT_DECL)
    { 
        //print_tree_code_name(t);
        if (DECL_NAME(t))
            printf("Name = %s\n", IDENTIFIER_POINTER(DECL_NAME(t)) );
        else
            printf("NAME = TEMP-%d\n", DECL_UID(t));
    }
    else if ( TREE_CODE(t) == CALL_EXPR || TREE_CODE(t) == AGGR_INIT_EXPR)
    {
        //print_tree_code_name(t);
        printf("NAME = %s\n", SSPC_fun_call_name(TREE_OPERAND(t,1)) );

    }
    return NULL;
}
void SSPC_hook_expr_fun(tree * tp, sspc_data * dp, int *ws)
{
	tree t = *tp;
	sspc_sql sql_data;
	int n = TREE_OPERAND_LENGTH(t), i;
	sql_data.t = tp;
	sql_data.op[0] = 0;
	sql_data.op[1] = 0;
	sql_data.op[2] = 0;
	sql_data.number = 0;
	sql_data.decl_name = "NULL";
        sprintf(sql_data.value, "NULL");
	for (i = 0; i < n; i++)
	{
		if (TREE_OPERAND(t, i))
		{
			sql_data.op[i] = (unsigned int)TREE_OPERAND(t, i);
		}
	}
	sql_add_ssp(dp->conn, dp->In_Function, sql_data);
}

void SSPC_hook_type_fun(tree * tp, sspc_data * dp, int *ws){
    tree t = *tp;
    sspc_sql sql_data;
    sql_data.t = tp;
    sql_data.op[0] = 0;
    sql_data.op[1] = 0;
    sql_data.op[2] = 0;
    sql_data.number = 0;
    sql_data.decl_name = "NULL";
    sprintf(sql_data.value, "NULL");
}

void SSPC_hook_const_fun(tree * tp, sspc_data * dp, int *ws){
    tree t = *tp;
    sspc_sql sql_data;
    sql_data.t = tp;
    sql_data.op[0] = 0;
    sql_data.op[1] = 0;
    sql_data.op[2] = 0;
    sql_data.number = 0;
    sql_data.decl_name = "NULL";
    sprintf(sql_data.value, "NULL");
    if(TREE_CODE(t) == INTEGER_CST){
        if( TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE ) {
            sprintf(sql_data.value, "%dd", TREE_INT_CST_LOW(t));
	} 
        else if( ! host_integerp (t, 0) ) {
	    tree val = t;
	    unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (val);
	    HOST_WIDE_INT high = TREE_INT_CST_HIGH (val);
	    if( tree_int_cst_sgn (val) < 0 ) {
		sprintf(sql_data.value, "-");
		high = ~high + !low;
		low = -low;
	    }
            sprintf(sql_data.value, "%d:%d", high, low);
        }
        else
        {
            sprintf(sql_data.value, "%d", TREE_INT_CST_LOW(t) );
        }
    }

    else if(TREE_CODE(t) == REAL_CST)
    {
        REAL_VALUE_TYPE d;
        if( TREE_OVERFLOW (t) )
	    sprintf (sql_data.value, "overflow");
#if !defined(REAL_IS_NOT_DOUBLE) || defined(REAL_ARITHMETIC)
	d = TREE_REAL_CST (t);
        //printf("TYPE1\n");
	if( REAL_VALUE_ISINF (d) )
	    sprintf (sql_data.value, REAL_VALUE_NEGATIVE (d) ? " -Inf" : " Inf");
	else if( REAL_VALUE_ISNAN (d) )
    	    sprintf (sql_data.value, "Nan");
        else {
    	    char string[100];
            real_to_decimal (string, &d, sizeof (string), 0, 1);
            sprintf(sql_data.value, "%s", string);
	}
#else
	{
            //printf("TYPE2\n");
	    HOST_WIDE_INT i;
	    unsigned char *p = (unsigned char *) &TREE_REAL_CST (node);
	    sprintf (sql_data.value, "0x");
	    for( i = 0; i < sizeof TREE_REAL_CST (node); i++ )
		    sprintf (sql_data.value, "%02x", *p++);
	}
#endif
    }
    
    else if(TREE_CODE(t) == STRING_CST)
    {
        sprintf(sql_data.value, "%s", TREE_STRING_POINTER(t));
    }
    //printf("%s\n", sql_data.value);
    sql_add_ssp(dp->conn, dp->In_Function, sql_data);

}
void SSPC_hook_decl_fun(tree * tp, sspc_data * dp, int *ws)
{
	tree t = *tp;
        tree node;
	sspc_sql sql_data;
        char *str;
	sql_data.t = tp;
	sql_data.op[0] = 0;
	sql_data.op[1] = 0;
	sql_data.op[2] = 0;
	sql_data.number = 0;
	sql_data.decl_name = "NULL";
        sprintf(sql_data.value, "NULL");
        if (DECL_NAME(t))
	{
	    sql_data.decl_name = IDENTIFIER_POINTER(DECL_NAME(t));
	}
        node = TREE_TYPE(t);
        //print_tree_code_name(node);
        switch( TREE_CODE(node) ){
            case VOID_TYPE:
	    case INTEGER_TYPE:
	    case REAL_TYPE:
	    case FIXED_POINT_TYPE:
	    case COMPLEX_TYPE:
	    case VECTOR_TYPE:
	    case ENUMERAL_TYPE:
	    case BOOLEAN_TYPE:
	    {
		unsigned int quals = TYPE_QUALS (node);
                if( quals & TYPE_QUAL_CONST )
		    sprintf ( sql_data.value, "const ");
		else if( quals & TYPE_QUAL_VOLATILE )
		    sprintf ( sql_data.value, "volatile ");
		else if( quals & TYPE_QUAL_RESTRICT )
		    sprintf ( sql_data.value, "restrict ");
		if( !ADDR_SPACE_GENERIC_P (TYPE_ADDR_SPACE (node)) ) {
		    sprintf ( sql_data.value, "<address-space-");
		    sprintf ( SQL_STRING_ADDR, "%s>", TYPE_ADDR_SPACE (node));
		}
                if(TYPE_NAME(node)){
		    if( TREE_CODE (TYPE_NAME(node)) == IDENTIFIER_NODE )
                        sprintf(sql_data.value, "%s\n", IDENTIFIER_POINTER( TYPE_NAME(node) ));
		    else if( TREE_CODE (TYPE_NAME (node)) == TYPE_DECL && DECL_NAME (TYPE_NAME (node)) )
                        sprintf(sql_data.value, "%s\n", IDENTIFIER_POINTER( DECL_NAME(TYPE_NAME(node) ) ));
		    else
                        sprintf(sql_data.value, "<unamed type>");
		}/* 
                else if( TREE_CODE (node) == VECTOR_TYPE ) {
		    pp_string (buffer, "vector");
		    pp_character (buffer, '(');
		    pp_wide_integer (buffer, TYPE_VECTOR_SUBPARTS (node));
		    pp_string (buffer, ") ");
		    dump_generic_node (buffer, TREE_TYPE (node), spc, flags, false);
		}*/ 
                else if( TREE_CODE (node) == INTEGER_TYPE ) {
		    sprintf (sql_data.value, TYPE_UNSIGNED (node) ? "<unnamed-unsigned:" : "<unnamed-signed:");
		    sprintf (SQL_STRING_ADDR, "%d>",  TYPE_PRECISION (node));
		}/* 
                else if( TREE_CODE (node) == COMPLEX_TYPE ) {
		    pp_string (buffer, "__complex__ ");
		    dump_generic_node (buffer, TREE_TYPE (node), spc, flags, false);
		} else if( TREE_CODE (node) == REAL_TYPE ) {
		    pp_string (buffer, "<float:");
		    pp_decimal_int (buffer, TYPE_PRECISION (node));
		    pp_string (buffer, ">");
		} else if( TREE_CODE (node) == FIXED_POINT_TYPE ) {
		    pp_string (buffer, "<fixed-point-");
		    pp_string (buffer, TYPE_SATURATING (node) ? "sat:" : "nonsat:");
		    pp_decimal_int (buffer, TYPE_PRECISION (node));
		    pp_string (buffer, ">");
		} else if( TREE_CODE (node) == VOID_TYPE )
		    pp_string (buffer, "void");
		else
		    pp_string	(buffer, "<unnamed type>");
		}*/
	        break;
	    } 
            case POINTER_TYPE:
	    case REFERENCE_TYPE:
            case ARRAY_TYPE:
                if (TREE_CODE(node) == POINTER_TYPE)
                    str = "*";
                else if(TREE_CODE(node) == REFERENCE_TYPE)
                    str = "&";
                else 
                    str = "";

		sprintf (sql_data.value, str);
		if( TREE_TYPE (node) == NULL ) {
			sprintf (sql_data.value + strlen(sql_data.value), "<null type>");
		} 
                else if( TREE_CODE (TREE_TYPE (node)) == FUNCTION_TYPE ) {
			tree fnode = TREE_TYPE (node);
			if( TYPE_NAME (node) && DECL_NAME (TYPE_NAME (node)) )
			{
                            sprintf(SQL_STRING_ADDR, "%s\n", IDENTIFIER_POINTER( DECL_NAME(TYPE_NAME(node) ) ));
                        }    
			//dump_function_declaration (buffer, fnode, spc, flags);
		} 
                else {
			unsigned int quals = TYPE_QUALS (node);

			//dump_generic_node (buffer, TREE_TYPE (node), spc, flags, false); 
                        if(TREE_TYPE(node)){
                            if(TYPE_NAME(TREE_TYPE(node))){
                                tree val = TREE_TYPE(node);
		                if( TREE_CODE (TYPE_NAME(val)) == IDENTIFIER_NODE )
                                    sprintf(SQL_STRING_ADDR, "%s\n", IDENTIFIER_POINTER( TYPE_NAME(val) ));
		                else if( TREE_CODE (TYPE_NAME (val)) == TYPE_DECL && DECL_NAME (TYPE_NAME (val)) )
                                    sprintf(SQL_STRING_ADDR, "%s\n", IDENTIFIER_POINTER( DECL_NAME(TYPE_NAME(val) ) ));
		                else
                                    sprintf(SQL_STRING_ADDR, "<unamed type>");
                            }
		        }
		}/*
                if(TREE_CODE(node) == ARRAY_TYPE)
                {
                    printf("[%d]\n", TYPE_DOMAIN(node));
                }*/
		break;

            case RECORD_TYPE:
	    case UNION_TYPE:
	    case QUAL_UNION_TYPE:
	    {
			unsigned int quals = TYPE_QUALS (node);
                        print_tree_code_name(node);

			if( quals & TYPE_QUAL_CONST )
				sprintf (sql_data.value, "const ");
			if( quals & TYPE_QUAL_VOLATILE )
				sprintf (sql_data.value, "volatile ");

			/* Print the name of the structure.  */
			if( TREE_CODE (node) == RECORD_TYPE )
				sprintf (sql_data.value, "struct ");
			else if( TREE_CODE (node) == UNION_TYPE )
				sprintf (sql_data.value, "union ");

			if( TYPE_NAME (node) ){
				//dump_generic_node (buffer, TYPE_NAME (node), spc, flags, false);
                                sprintf (SQL_STRING_ADDR, "%s", IDENTIFIER_POINTER(TYPE_NAME(node)));
                        }
                        break;
	    }
        }
	sql_add_ssp(dp->conn, dp->In_Function, sql_data);
}



void SSPC_hook_stmt_fun(tree * tp, sspc_data * dp, int *ws)
{
	sql_stmt_list(tp, dp->In_Function, dp->conn);
}



void SSPC_hook_call_fun(tree * tp, sspc_data * dp, int *ws)
{
	sql_call(tp, dp->In_Function, dp->conn);
}



plugin_callback_func exit_callback(void *gcc_data, void *user_data)
{
	printf("//In exit_callback \n");
	exit(1);
}

plugin_callback_func 
sspc_callback(void *gcc_data, void *user_data)
{
        //FILE *fp;
        char res;
	sspc_data data;
	int i = 0;
	tree t = (tree *) gcc_data;
	tree tt = DECL_SAVED_TREE(t);
	tree decl_t;
        char dbname[100];
	char sql_name[500];
        sprintf(dbname, "SSP_AST_%s", (char *)user_data);
	sprintf(sql_name, "`%s@%s`", IDENTIFIER_POINTER(DECL_NAME(t)), aux_base_name);
        printf("[AST Generating ... ]\n");
        printf("db name:%s\ntable name[%d]:%s\n", (char *)user_data, strlen(sql_name), sql_name);
        //printf("DECL_SOURCE_LINE = %d\n", DECL_SOURCE_LINE(t));

        data.In_Function = sql_name;
        data.conn = mysql_init(NULL);
        sql_create_database(data.conn, dbname);
        sql_create_table(data.conn, data.In_Function);
        walk_tree(&tt, ssp_func_walk, &data, NULL);
        //walk_tree(&tt, ssp_func_trace, &data, NULL);
        sql_del_dup(data.conn, data.In_Function);
        sql_ssp_pre_1(data.conn, data.In_Function);
	mysql_close(data.conn);
        
        //fp = fopen("prettyprint.out", "a+");
        //fprintf(fp, "%s", data.In_Function);
        //print_c_tree(fp, t);
        //print_c_tree(fp, tt);
        //fclose(fp);
        
}

void sql_create_database(MYSQL * conn, char *dbname)
{
	char sql_buf[500];
	char *server = SQLSERVER;
	char *user = SQLUSER;
	char *password = SQLPASS;
	char *database = NULL;
	int n;
	if (!mysql_real_connect
		(conn, server, user, password, database, 0, NULL, CLIENT_MULTI_STATEMENTS))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
	n = sprintf(sql_buf, "create database if not exists %s", dbname);
	//printf("sql query[%d] = %s\n", n, sql_buf);
	if (mysql_query(conn, sql_buf))
	{
		printf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}

	if (!mysql_real_connect(conn, server, user, password, dbname, 0, NULL, 0))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
}

void sql_create_table(MYSQL * conn, char *name)
{
	char sql_buf[500];
	int n;
	n = sprintf(sql_buf,
				"CREATE TABLE if not exists %s (id BIGINT NOT NULL AUTO_INCREMENT,addr BIGINT NOT NULL, type CHAR(40) NOT NULL, op0 BIGINT, op1 BIGINT, op2 BIGINT, name CHAR(30),  number INT, var_type char(40) NOT NULL, value char(100) ,PRIMARY KEY (id))",
				name);
	//printf("sql query[%d] = %s\n", n, sql_buf);
	if (mysql_query(conn, sql_buf))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
}

tree tree_type(tree t){
    if (TREE_CODE(t) == LABEL_DECL || TREE_CODE(t) == HANDLER)
        return "NULL";
    else
        return tree_code_name[TREE_CODE(TREE_TYPE(t))];
}
void sql_add_ssp(MYSQL * conn, char *name, sspc_sql sql)
{
	char *sql_buf = xmalloc(400*sizeof(char));
	int n;
	const char *content = "(addr, type, op0, op1, op2, name, number, var_type, value)";
        //printf("start\n");
	n = sprintf(sql_buf,
				"INSERT INTO %s %s values ( %u, '%s', %u, %u, %u, '%s', %u, '%s', '%s')",
				name, content, (unsigned int)(*sql.t),
				tree_code_name[TREE_CODE(*(sql.t))], sql.op[0], sql.op[1],
				sql.op[2], sql.decl_name, sql.number, tree_type(*sql.t), sql.value);
	//printf("sql query[%d] = %s\n", n, sql_buf);
        //print_tree_code_name(*sql.t);
        //printf("\t");
        //print_tree_code_name(TREE_TYPE(*sql.t));
	if (mysql_query(conn, sql_buf))
	{
		printf("%s\n", mysql_error(conn));
		exit(1);
	}
        free(sql_buf);
}

void sql_stmt_list(tree * tp, char *name, MYSQL * conn)
{
	tree_stmt_iterator it1;
	tree t = *tp;
	sspc_sql sql_data;
	sql_data.t = tp;
	sql_data.op[0] = 0;
	sql_data.op[1] = 0;
	sql_data.op[2] = 0;
	sql_data.number = 0;
	sql_data.decl_name = "NULL";
        sprintf(sql_data.value, "NULL");
	int i = 0;
	for (it1 = tsi_start(t); !tsi_end_p(it1); tsi_next(&it1))
	{
		if (tsi_stmt(it1))
		{
			//printf("stmt[%d] %u\n", i, (unsigned int)tsi_stmt_ptr(it1));
			//print_tree_code_name(*tsi_stmt_ptr(it1));
			sql_data.number = i++;
			sql_data.op[0] = (unsigned int)*tsi_stmt_ptr(it1);
			sql_add_ssp(conn, name, sql_data);
		}
	}
}

void sql_call(tree * tp, char *name, MYSQL * conn)
{
	tree t = *tp;
	int n = call_expr_nargs(t), i;
	sspc_sql sql_data;
	sql_data.t = tp;
	sql_data.op[0] = 0;
	sql_data.op[1] = 0;
	sql_data.op[2] = 0;
	sql_data.number = 0;
	sql_data.decl_name = "NULL";
        sprintf(sql_data.value, "NULL");
	//printf("call_fun name = %s\n", SSPC_fun_call_name(CALL_EXPR_FN(t)));
	sql_data.decl_name = SSPC_fun_call_name(CALL_EXPR_FN(t));
	sql_data.op[0] = CALL_EXPR_FN(t);
	sql_add_ssp(conn, name, sql_data);
	sql_data.decl_name = "ARG";
	for (i = 0; i < n; i++)
	{
		if (CALL_EXPR_ARG(t, i))
		{
			//printf("call_arg[%d] %u\n", i, (unsigned int)CALL_EXPR_ARG(t, i));
			sql_data.number = i;
			sql_data.op[0] = (unsigned int)CALL_EXPR_ARG(t, i);
			sql_add_ssp(conn, name, sql_data);
		}
	}
}

void sql_del_dup(MYSQL * conn, char *name){
    
	char sql_buf[500];
	int n;
	n = sprintf(sql_buf,
				"CREATE TABLE if not exists temp as select distinct addr, type, op0, op1, op2, name, number, var_type, value from %s", name);
	//printf("sql query[%d] = %s\n", n, sql_buf);
	if (mysql_query(conn, sql_buf))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
	n = sprintf(sql_buf,
				"alter table temp add id INT(11) NOT NULL AUTO_INCREMENT primary key FIRST");
	//printf("sql query[%d] = %s\n", n, sql_buf);
	if (mysql_query(conn, sql_buf))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
	n = sprintf(sql_buf,
				"drop table %s", name);
	//printf("sql query[%d] = %s\n", n, sql_buf);
	if (mysql_query(conn, sql_buf))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
	n = sprintf(sql_buf,
				"rename table temp to %s", name);
	//printf("sql query[%d] = %s\n", n, sql_buf);
	if (mysql_query(conn, sql_buf))
	{
		fprintf(stderr, "%s\n", mysql_error(conn));
		exit(1);
	}
}

/*The following code is contributed to Guang at Jun.13
 * The whole batch of SQL Language will reorgnize the index of the node*/
void sql_ssp_pre_1(MYSQL *conn, char *name){
    char *sql_buf = xmalloc (2000 * sizeof(char) );
    int n;
    SQL_EXEC("DROP TABLE IF EXISTS tEmpssP");
    SQL_EXEC("DROP PROCEDURE IF EXISTS ref_ch");
    n = sprintf(sql_buf, "CREATE TABLE tEmPssP LIKE %s", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "INSERT INTO tEmPssP SELECT * FROM %s", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s ADD ref_id INT(11) AFTER id", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s ADD op0_id INT(11) AFTER op0", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s ADD op1_id INT(11) AFTER op1", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s ADD op2_id INT(11) AFTER op2", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf,"CREATE PROCEDURE ref_ch() BEGIN\
            DECLARE c_ref_id INT;\
            DECLARE c_addr BIGINT(20);\
            DECLARE done INT DEFAULT 0;\
            DECLARE cur CURSOR FOR SELECT DISTINCT id, addr FROM tEmPssP;\
            DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;\
            OPEN cur; rp_loop: LOOP FETCH cur INTO c_ref_id, c_addr;\
            IF done THEN LEAVE rp_loop; END IF;\
            UPDATE %s SET ref_id=c_ref_id WHERE addr= ANY (SELECT c_addr FROM tEmPssP);\
            UPDATE %s SET op0_id=c_ref_id WHERE op0= ANY (SELECT c_addr FROM tEmPssP);\
            UPDATE %s SET op1_id=c_ref_id WHERE op1= ANY (SELECT c_addr FROM tEmPssP);\
            UPDATE %s SET op2_id=c_ref_id WHERE op2= ANY (SELECT c_addr FROM tEmPssP);\
            END LOOP; CLOSE cur; END\
            ", name, name, name, name);
    //printf("sql query[%d] = %s\n", n, sql_buf);

    if (mysql_query(conn, sql_buf)){
        fprintf(stderr, "%s\n", mysql_error(conn));
    }

    SQL_EXEC("CALL ref_ch()");

    n = sprintf(sql_buf, "ALTER TABLE %s DROP addr", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s DROP op0", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s DROP op1", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s DROP op2", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s CHANGE ref_id addr INT(11)", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s CHANGE op0_id op0 INT(11)", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s CHANGE op1_id op1 INT(11)", name);
    SQL_EXEC(sql_buf);

    n = sprintf(sql_buf, "ALTER TABLE %s CHANGE op2_id op2 INT(11)", name);
    SQL_EXEC(sql_buf);
    SQL_EXEC("DROP TABLE IF EXISTS tEmPssP");
    SQL_EXEC("DROP PROCEDURE IF EXISTS ref_ch");

    free(sql_buf);

}

char *fun_decl2string(tree t){
    if(TREE_CODE(t) == FUNCTION_DECL)
        return IDENTIFIER_POINTER(DECL_NAME(t));
}
void whocalledme(struct cgraph_node *node){
    printf("FUN_CALL = %s\n", fun_decl2string(node->decl));
    if(node->callers != NULL)
        whocalledme(node->callers->caller);
}
plugin_callback_func 
io_callback(void *gcc_data, void *user_data)
{
    struct cgraph_node *node;
    struct varpool_node *vnode;
    node = cgraph_nodes;
    vnode = varpool_nodes;
    while(node){
        whocalledme(node);
        printf("END\n");
        node = node -> next;
    }
    while(vnode)
    printf("in io_callback function\n");
}

void ssp_ssa_info(tree t){
    if(TREE_CODE(t) == SSA_NAME){
        ssp_decl_info(SSA_NAME_VAR(t));
    }
}
void ssp_decl_info(tree t){
    print_tree_code_name(t);
    if(DECL_P(t)){
        if(DECL_NAME(t))
            printf("name = %s\n", IDENTIFIER_POINTER(DECL_NAME(t)));
        else
            printf("name = TEMP.%d\n", DECL_UID(t));
    }
}

void 
sql_create_gimple_tbl(MYSQL *conn, char *name){
    char *sql_buf = xmalloc(500 * sizeof(char));
    int n;
    n = sprintf(sql_buf,\
            "Create Table if not exists %s (id BIGINT NOT NULL\
            AUTO_INCREMENT, type CHAR(30) NOT NULL, op0 CHAR(50),\
            op1 CHAR(50), op2 CHAR(50), block BIGINT NOT NULL,\
            action CHAR(50) NOT NULL, PRIMARY KEY (id) )", name);
    //printf("sql query[%d] = %s\n", n, sql_buf);
    SQL_EXEC(sql_buf);
    free(sql_buf);
}

int 
check_buildin_fun(char *name){
    if (!strcmp(name, "operator=")){
        return 1;
    }
    else if (!strcmp(name, "operator+=")){
        return 1;
    }
    else if (!strcmp(name, "memcpy")){
        return 1;
    }
    else 
        return 0;
}
void 
sql_add_gimple(MYSQL *conn, char *name, ssp_gimple_data *sg){
    char *sql_buf = xmalloc(500 * sizeof(char));
    int n;
    const char *content = "(type, op0, op1, op2, block, action)";
    if (!sg->op_more){
        n = sprintf(sql_buf, "INSERT INTO %s %s values ('%s', '%s',\
            '%s', '%s', %u, '%s')", name, content, sg->type, sg->op0, sg->op1,\
            sg->op2, sg->block, sg->action);
        //printf("sql query[%d] = %s\n", n, sql_buf);
        SQL_EXEC(sql_buf);
    }
    /*if the statment have more than 3 operators */
    else{
        //print_gimple(sg->g);
        gimple g = sg->g;
        if(gimple_code(g) == GIMPLE_PHI){
            int num = gimple_phi_num_args(g);
            int i;
            for (i=0;i<num;i++){
                tree arg = gimple_phi_arg_def(g, i);
                char *arg_s = tree2string(arg);
                n = sprintf(sql_buf, "INSERT INTO %s %s values ('%s', '%s',\
                    '%s', '%s', %u, '%s')", name, content, sg->type, sg->op0, arg_s,\
                    "NULL", sg->block, sg->action);
                //printf("sql query[%d] = %s\n", n, sql_buf);
                SQL_EXEC(sql_buf);
            }
        }
        if(gimple_code(g) == GIMPLE_ASSIGN){
            n = sprintf(sql_buf, "INSERT INTO %s %s values ('%s', '%s',\
                '%s', '%s', %u, '%s')", name, content, sg->type, sg->op0, sg->op1,\
                sg->op2, sg->block, sg->action);
            //printf("sql query[%d] = %s\n", n, sql_buf);
            SQL_EXEC(sql_buf);
            //printf("op_more = %d\n", sg->op_more);
            if(sg->t){
                //print_tree_code_name(sg->t);
                tree start = TREE_OPERAND(sg->t, 0);
                sg->type = "gimple_array";
                if(TREE_CODE(start) == ARRAY_REF){
                    ssp_gimple_data *sspg = xmalloc(sizeof(ssp_gimple_data));
                    sspg->g = sg->g;
                    sspg->type = sg->type;
                    sspg->block = sg->block;
                    sspg->action = sg->action;
                    sspg->op_more = sg->op_more;
                    sspg->t = start;
                    sspg->op0 = sg->op0;
                    sspg->op1 = sg->op1;
                    sspg->op2 = sg->op2;
                    sspg->op0_more = tree2string(start);
                    sql_add_gimple(conn, name, sspg);
                    free(sspg);
                }
                tree end =   TREE_OPERAND(sg->t, 1);
                char *op0 = tree2string(sg->t);
                char *op1 = tree2string(start);
                char *op2 = tree2string(end);
                n = sprintf(sql_buf, "INSERT INTO %s %s values ('%s', '%s',\
                    '%s', '%s', %u, '%s')", name, content, sg->type, sg->op0_more, op1,\
                    op2, sg->block, "array_comp");
                //printf("sql query[%d] = %s\n", n, sql_buf);
                SQL_EXEC(sql_buf);
            }
        }
        if(gimple_code(g) == GIMPLE_CALL){
            int num = gimple_call_num_args(g);
            int i;
            char lhs[100];
            printf("fun name =%s\n", sg->op0);
            if (check_buildin_fun(sg->op0)){
                tree temp_call = gimple_call_arg(g,0);
                strcpy(lhs, tree2string(temp_call));
            }
            else
                strcpy(lhs,  sg->op1);
            for (i=0; i<num; i++){
                tree arg = gimple_call_arg(g, i); 
                char *arg_s = tree2string(arg);
                printf("lhs = %s\n", lhs);
                //printf("ARG[%d]: %s\n", i, arg_s);
                n = sprintf(sql_buf, "INSERT INTO %s %s values ('%s', '%s',\
                    '%s', '%s', %u, '%s')", name, content, sg->type, lhs, arg_s,\
                    "NULL", sg->block, sg->op0);
                SQL_EXEC(sql_buf);
            }
        }
    }
    free(sql_buf);
}
char *tree2string(tree t){
    //printf("[debug tree2string]\t");
    //print_tree_code_name(t);
    if (t == NULL_TREE){
        return "NULL";
    }
    else if(CONSTANT_CLASS_P(t)){
        static char ret[100] = "CONST_";
        //printf("%s\n", ret);
        if(TREE_CODE(t) == INTEGER_CST){
            tree2num(t, ret + 6);
        }
        else if(TREE_CODE(t) == REAL_CST){
            tree2num(t, ret + 6);
        }
        else {
            print_tree_code_name(t);
        }
        return ret;
    }
    else if(TREE_CODE(t) == ADDR_EXPR){
        return tree2string(TREE_OPERAND(t,0));
    }
    else if(REFERENCE_CLASS_P(t)){
        if (TREE_CODE(t) == ARRAY_REF || TREE_CODE(t) == COMPONENT_REF){
            static char temp[400];
            static char temp2[400];
            strcpy(temp, tree2string(TREE_OPERAND(t,0)));
            if (TREE_CODE(t) == ARRAY_REF)
                sprintf(temp2, "[%s]", tree2string(TREE_OPERAND(t,1)));
            else
                sprintf(temp2, "->%s", tree2string(TREE_OPERAND(t,1)));
            strcat(temp, temp2);
            return temp;
        }
        else{
            return tree2string(TREE_OPERAND(t,0));
        }
    }
    else if (TREE_CODE(t) == SSA_NAME){
        static char temp1[100];
        strcpy(temp1, tree2string(SSA_NAME_VAR(t)));
        char temp2[100];
        sprintf(temp2, ".%d", SSA_NAME_VERSION(t));
        strcat(temp1, temp2);
        return temp1;
    }
    else if (DECL_P(t)){
        static char temp[100];
        if(TREE_CODE(t) == PARM_DECL)
            strcpy(temp, "PARM_");
        else
            strcpy(temp, "");
        if(DECL_NAME(t)){
            sprintf(temp + strlen(temp), "%s",IDENTIFIER_POINTER(DECL_NAME(t)));
        }
        else {
            sprintf(temp + strlen(temp), "T.%d", DECL_UID(t));
        }
        return temp;
    }
    else
        return "NULL";
}

void
ssp_gimple(ssp_gimple_data *sspg){
    unsigned int i;
    char *str1 = xmalloc(100*sizeof(char));
    char *str2 = xmalloc(100*sizeof(char));
    char *str3 = xmalloc(100*sizeof(char));
    strcpy(str1, "NULL");
    strcpy(str2, "NULL");
    strcpy(str3, "NULL");
    gimple g = sspg->g;
    sspg->op0 = str1;
    sspg->op1 = str2;
    sspg->op2 = str3;
    sspg->type = NULL;
    sspg->block = (unsigned int)gimple_bb(g)->index;
    sspg->action = NULL;
    sspg->op_more = 0;
    if (is_gimple_assign(g)){
        tree lhs = gimple_assign_lhs(g);
        tree rhs1 = gimple_assign_rhs1(g);
        tree rhs2 = gimple_assign_rhs2(g);
        sspg->type = gimple_code_name[gimple_code(g)];
        strcpy(str1, tree2string(lhs));
        strcpy(str2, tree2string(rhs1));
        strcpy(str3, tree2string(rhs2));
        sspg->op0 = str1;
        sspg->op1 = str2;
        sspg->op2 = str3;
        sspg->action = tree_code_name[gimple_assign_rhs_code(g)];
        if(strcmp(str1, "NULL")){
            if( TREE_CODE(lhs) == ARRAY_REF){
                sspg->op0_more = sspg->op0;
                sspg->op_more = 1;
                sspg->t = lhs;
                printf("%s\n", str1);
            }
        }
        if(strcmp(str2, "NULL")){
            if( TREE_CODE(rhs1) == ARRAY_REF){
                sspg->op0_more = sspg->op1;
                printf("%s\n", str2);
                sspg->op_more = 1;
                sspg->t = rhs1;
            }
        }
        if(strcmp(str3, "NULL")){
            if( TREE_CODE(rhs2) == ARRAY_REF){
                sspg->op0_more = sspg->op2;
                printf("%s\n", str3);
                sspg->op_more = 1;
                sspg->t = rhs2;
            }
        }
    }
    else if(is_gimple_call(g)){
        sspg->op_more = 1;
        tree lhs = gimple_call_lhs(g);
        tree fn = gimple_call_fndecl(g);
        sspg->type = gimple_code_name[gimple_code(g)];
        strcpy(str1, tree2string(fn));
        strcpy(str2, tree2string(lhs));
        sspg->op0 = str1;
        sspg->op1 = str2;
        sspg->op2 = str3; 
        sspg->action = tree2string(fn);
    }
    else if(gimple_code(g) == GIMPLE_RETURN){
        tree ret = gimple_return_retval(g);
        sspg->type = gimple_code_name[gimple_code(g)];
        strcpy(str1, tree2string(ret));
        sspg->op0 = str1;
        sspg->op1 = str2;
        sspg->op2 = str3; 
        sspg->action = NULL;
    }
    else if(gimple_code(g) == GIMPLE_COND){
        tree lhs = gimple_cond_lhs(g);
        tree rhs = gimple_cond_rhs(g);
        sspg->type = gimple_code_name[gimple_code(g)];
        strcpy(str1, tree2string(lhs));
        strcpy(str2, tree2string(rhs));
        sspg->op0 = str1; 
        sspg->op1 = str2; 
        sspg->op2 = str3; 
        sspg->action = tree_code_name[gimple_cond_code(g)];
    }
    else if(gimple_code(g) == GIMPLE_PHI){
        sspg->op_more = 1;
        tree lhs = gimple_phi_result(g);
        tree arg1 = gimple_phi_arg_def(g,0);
        tree arg2 = gimple_phi_arg_def(g,1);
        sspg->type = gimple_code_name[gimple_code(g)];
        strcpy(str1, tree2string(lhs));
        strcpy(str2, tree2string(arg1));
        strcpy(str3, tree2string(arg2));
        sspg->op0 = str1;
        sspg->op1 = str2;
        sspg->op2 = str3;
        //sspg->op0 = tree2string(lhs); 
        //sspg->op1 = tree2string(arg1);
        //printf("%s(%d)\n", sspg->op1, gimple_phi_arg_edge(g,0)->src->index);
        //sspg->op2 = tree2string(arg2);
        //printf("%s(%d)\n", sspg->op2, gimple_phi_arg_edge(g,1)->src->index);
        /*GIMPLE_PHI may have more than 2 candidate FIXME future*/
    }
    else {
       ; 
    }
}
static void 
bb_index(basic_block bb){
    //printf("\nbb:%d\n", bb->index);
}
void 
bb_ssp_pred(basic_block bb, MYSQL *conn, char *tbname){
    ssp_gimple_data *sspg = xmalloc(sizeof(ssp_gimple_data));
    int i,e[3]={-1,-1,-1};
    char *str0 = xmalloc(100*sizeof(char));
    char *str1 = xmalloc(100*sizeof(char));
    char *str2 = xmalloc(100*sizeof(char));
    for(i=0; i<EDGE_COUNT(bb->preds);i++){
        e[i] = (EDGE_PRED(bb,i) ? EDGE_PRED(bb,i)->src->index : -1);
    }
    //printf("pred = %d %d %d\n", e[0], e[1], e[2]);
    if (e[0] != -1)
        sprintf(str0, "%d", e[0]);
    else
        sprintf(str0, "NULL");
    if (e[1] != -1)
        sprintf(str1, "%d", e[1]);
    else
        sprintf(str1, "NULL");
    if (e[2] != -1)
        sprintf(str2, "%d", e[2]);
    else
        sprintf(str2, "NULL");
    sspg->op0 = str0;
    sspg->op1 = str1;
    sspg->op2 = str2;
    sspg->g = NULL;
    sspg->type = "gimple_pred";
    sspg->action = NULL;
    sspg->block = bb->index;
    sspg->op_more = 0;
    sql_add_gimple(conn, tbname, sspg);
    free(str0);
    free(str1);
    free(str2);
    free(sspg);

}
void
bb_ssp_succ(basic_block bb, MYSQL *conn, char *tbname){
    ssp_gimple_data *sspg = xmalloc(sizeof(ssp_gimple_data));
    int i,e[3]={-1,-1,-1};
    char *str0 = xmalloc(100*sizeof(char));
    char *str1 = xmalloc(100*sizeof(char));
    char *str2 = xmalloc(100*sizeof(char));
    for(i=0; i<EDGE_COUNT(bb->succs);i++){
        e[i] = (EDGE_SUCC(bb,i) ? EDGE_SUCC(bb,i)->dest->index : -1);
    }
    //printf("succ = %d %d %d\n", e[0], e[1], e[2]);
    if (e[0] != -1)
        sprintf(str0, "%d", e[0]);
    else
        sprintf(str0, "NULL");
    if (e[1] != -1)
        sprintf(str1, "%d", e[1]);
    else
        sprintf(str1, "NULL");
    if (e[2] != -1)
        sprintf(str2, "%d", e[2]);
    else
        sprintf(str2, "NULL");
    sspg->op0 = str0;
    sspg->op1 = str1;
    sspg->op2 = str2;
    sspg->g = NULL;
    sspg->type = "gimple_succ";
    sspg->action = NULL;
    sspg->block = bb->index;
    sspg->op_more = 0;
    sql_add_gimple(conn, tbname, sspg);
    free(str0);
    free(str1);
    free(str2);
    free(sspg);
}
int 
check_name(char *name){
    if (!strcmp(name, "work")){
        printf("in work function, continue ...\n");
        return 0;
    }
    else {
       return 1; 
    }
}
unsigned int 
execute_ssp_io(void){
    //FILE *fp;
    //fp = fopen("out","a+");
    gimple_stmt_iterator gsi;
    basic_block bb;
    struct opt_pass *pass =  current_pass;
    char dbname[100];
    char tbname[500];
    sprintf(dbname, "SSP_GIMPLE_%s", pass->name);
    printf("dbname = %s\n", dbname);
    sprintf(tbname, "`%s@%s`", IDENTIFIER_POINTER(DECL_NAME(cfun\
                    ->decl)), aux_base_name );
    printf("tbname = %s\n", tbname);
    if (check_name(IDENTIFIER_POINTER(DECL_NAME(cfun->decl)))){
        printf("ignore ...\n");
        return 0;
    }

    MYSQL *conn = mysql_init(NULL);
    sql_create_database(conn, dbname);
    sql_create_gimple_tbl(conn, tbname);
    FOR_EACH_BB(bb){
       bb_index(bb); 
       bb_ssp_pred(bb, conn, tbname);
       for( gsi = gsi_start_phis(bb); !gsi_end_p(gsi); gsi_next(&gsi)){
           gimple stmt = gsi_stmt(gsi);
           ssp_gimple_data *sspg = xmalloc(sizeof(ssp_gimple_data));
           sspg->g = stmt;
           ssp_gimple(sspg);
           sql_add_gimple(conn, tbname, sspg);
           free(sspg->op0);
           free(sspg->op1);
           free(sspg->op2);
           free(sspg);
       }
       for( gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)){
           gimple stmt = gsi_stmt(gsi);
           ssp_gimple_data *sspg = xmalloc(sizeof(ssp_gimple_data));
           sspg->g = stmt;
           ssp_gimple(sspg);
           sql_add_gimple(conn, tbname, sspg);
           free(sspg->op0);
           free(sspg->op1);
           free(sspg->op2);
           free(sspg);
       }
       bb_ssp_succ(bb, conn, tbname);
    }
    //gimple_dump_cfg(fp, 0);
    //fclose(fp);
    mysql_close(conn);
    printf("in ssp_io pass\n");
    return 0;
}
